package org.jvm.nativemethod.methods.java.lang;

import org.jvm.nativemethod.NativeMethodRegister;
import org.jvm.rtda.Object;
import org.jvm.rtda.heap.Klass;
import org.jvm.rtda.thread.*;
import org.jvm.util.BoxUtil;

import java.lang.reflect.Array;

/**
 * @author 海燕
 * @date 2023/3/26
 */
public class JavaLangReflectArrayNativeMethod extends NativeMethodRegister {

    private static String jlObject = "java/lang/reflect/Array";

    public static void init() {
        try {
            register(jlObject, "get", "(Ljava/lang/Object;I)Ljava/lang/Object;", JavaLangReflectArrayNativeMethod.class.getDeclaredMethod("get", Frame.class));
            register(jlObject, "getLength", "(Ljava/lang/Object;)I", JavaLangReflectArrayNativeMethod.class.getDeclaredMethod("getLength", Frame.class));
            register(jlObject, "newArray", "(Ljava/lang/Class;I)Ljava/lang/Object;", JavaLangReflectArrayNativeMethod.class.getDeclaredMethod("newArray", Frame.class));
            register(jlObject, "set", "(Ljava/lang/Object;ILjava/lang/Object;)V", JavaLangReflectArrayNativeMethod.class.getDeclaredMethod("set", Frame.class));
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

    public static void set(Frame frame) {
        LocalVars localVars = frame.getLocalVars();
        Object arr = localVars.getRef(0);
        int index = localVars.getInt(1);
        Object value = localVars.getRef(2);

        if (arr == null) {
            frame.getThread().throwNullPointerException();
            return;
        }
        if (!arr.getKlass().isArray()) {
            throw new RuntimeException("Argument is not an array");
        }
        if (index < 0 || index >= Array.getLength(arr.getData())) {
            throw new RuntimeException("ArrayIndexOutOfBoundsExceptionNoMsg");
        }
        java.lang.Object unboxed = BoxUtil.unbox(frame.getThread(), value);
        Array.set(arr.getData(), index, unboxed);
    }

    public static void get(Frame frame) {
        LocalVars localVars = frame.getLocalVars();
        Object arr = localVars.getRef(0);
        int index = localVars.getInt(1);

        if (arr == null) {
           frame.getThread().throwNullPointerException();
           return;
        }
        if (!arr.getKlass().isArray()) {
            throw new RuntimeException("Argument is not an array");
        }
        if (index < 0 || index >= Array.getLength(arr.getData())) {
            throw new RuntimeException("ArrayIndexOutOfBoundsExceptionNoMsg");
        }

        java.lang.Object arrayElement = Array.get(arr.getData(), index);
        Object boxed = BoxUtil.box(frame.getThread(), arrayElement);
        frame.getOperandStack().pushRef(boxed);
    }

    public static void getLength(Frame frame) {
        LocalVars localVars = frame.getLocalVars();
        Object arr = localVars.getRef(0);
        int length = Array.getLength(arr.getData());
        frame.getOperandStack().pushInt(length);
    }

    public static void newArray(Frame frame) {
        LocalVars localVars = frame.getLocalVars();
        Object componentType = localVars.getRef(0);
        int length = localVars.getInt(1);

        if (length < 0) {
            throw new RuntimeException("NegativeArraySizeException");
        }

        Klass componentClass = (Klass) componentType.getExtra();
        Object newArray = componentClass.arrayClass().newArray(length);
        frame.getOperandStack().pushRef(newArray);
    }


}
